import subprocess as sp
import logging
import tzlocal
from threading import Thread
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.events import EVENT_JOB_ERROR

from process import Process
from utils import OrderQueue, register_exit
from detect.detect import Detecter
from detect.drawer import Drawer


def _process_arg_list(l):
    ret = []
    for i in l:
        if i is not None:
            ret.append(i) if type(i) == str else ret.extend(i)
    return ret

def _construct_ffmpeg(config):
    return sp.Popen(_process_arg_list([
        'ffmpeg',
        '-y',
        '-fflags', 'nobuffer',
        '-f', 'rawvideo',
        '-vcodec','rawvideo',
        '-pix_fmt', 'bgr24',  # 我试过把ffmpeg的像素格式设为rgb24，结果就是帧率降低，可能BGR是ffmpeg的标准格式快一点
        '-s', f'{config.stream_w}x{config.stream_h}',
        '-r', str(config.fps if config.fps else config.stream_fps),
        '-i', '-',
        '-c:v', 'libx264',
        ('-b:v', f"{config.cbr}k") if config.cbr else None,
        '-pix_fmt', 'yuv420p',
        '-preset', 'ultrafast',
        '-f', 'flv',
        config.output]), stdin=sp.PIPE)

class ImagePusher(Process):

    def __init__(self, config, pusher_queue, error_queue):
        super().__init__()
        self._add_process(config, pusher_queue, error_queue)
        self._start()
    
    def _run(self, config, pusher_queue, error_queue):
        ffmpeg = _construct_ffmpeg(config)
        order_queue = OrderQueue(pusher_queue)
        del config

        while ffmpeg.poll() is None:
            _, frame = order_queue.get()
            try:
                if frame is not None:
                    ffmpeg.stdin.write(frame)
            except:
                error_queue.put('推流失败，请检查该地址是否还有其他程序在推流')
                return
        error_queue.put(f'FFmepg异常退出，无法推流，返回码为：{ffmpeg.returncode}')

class ResultsPusher(Process):

    def __init__(self, config, results_queue, stream_queue, error_queue):
        super().__init__()
        self._add_process(config, results_queue, stream_queue, error_queue)
        self._start()

    def _run(self, config, results_queue, stream_queue, error_queue):
        ffmpeg = _construct_ffmpeg(config)
        drawer = Drawer(config)

        self.results = None
        self.original_frame = None

        def push_frame():
            if ffmpeg.poll() is not None:
                error_queue.put('推流异常，FFMpeg异常退出')
            # 因为original_fram和results都会变，因此不做上一帧图像缓存了
            if self.original_frame is None:
                return
            try:
                ffmpeg.stdin.write(drawer.draw(self.original_frame, self.results))
            except Exception:
                error_queue.put('推流出现异常')
        
        sched = BackgroundScheduler(timezone=tzlocal.get_localzone_name())
        logging.getLogger('apscheduler').setLevel(logging.ERROR)  # 给这玩意的警告禁用掉，输出一大堆很烦人
        sched.add_job(push_frame, 'interval', max_instances=5, seconds=1/(config.fps if config.fps else config.stream_fps))
        sched.add_listener(lambda _: error_queue.put('推流出现异常'), EVENT_JOB_ERROR)
        sched.start()

        del config

        def pull_stream_forever():
            while True:
                self.original_frame = stream_queue.get()
        
        # 一直拉流
        Thread(target=pull_stream_forever, daemon=True).start()
        
        def push_quit():
            sched.shutdown()
            ffmpeg.terminate()
        
        register_exit(push_quit)

        # 一直获取预测结果
        while True:
            self.results = results_queue.get()
